#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "stdarg.h"

#include "BList.h"

#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"
#include "FT_refinement.h"

#include "SpecialTerm.h"

// #include "AWEntity.h"


List<FeatureTerm> *DSpecializationL0_b(FeatureTerm *base,List<FeatureTerm> *objects,Ontology *ontology,FTKBase *domain_model)
{
	return DSpecialization_b(base,objects,ontology,domain_model,0,true,true,false,false,true);
} /* DSpecializationL0_b */ 


List<FeatureTerm> *DSpecializationL1P_b(FeatureTerm *base,List<FeatureTerm> *objects,Ontology *ontology,FTKBase *domain_model)
{
	return DSpecialization_b(base,objects,ontology,domain_model,0,true,true,true,false,true);
} /* DSpecializationL1C_b */ 


List<FeatureTerm> *DSpecializationL1S_b(FeatureTerm *base,List<FeatureTerm> *objects,List<Path> *set_paths,Ontology *ontology,FTKBase *domain_model)
{
	return DSpecialization_b(base,objects,ontology,domain_model,0,true,true,false,true,true);
} /* DSpecializationL1S_b */ 


List<FeatureTerm> *DSpecializationL2_b(FeatureTerm *base,List<FeatureTerm> *objects,List<Path> *set_paths,Ontology *ontology,FTKBase *domain_model)
{
	return DSpecialization_b(base,objects,ontology,domain_model,0,true,true,true,true,true);
} /* DSpecializationL2_b */ 


List<FeatureTerm> *DSpecialization_b(FeatureTerm *base,List<FeatureTerm> *objects,Ontology *ontology,FTKBase *domain_model,
								     List<Path> *set_paths,
								     bool generate_sort_refinements,bool generate_feature_refinements,bool generate_equality_refinements,bool generate_sets_refinements,bool generate_costants_refinements)
{
	List<FeatureTerm> l1,l2,l3,l4,l5,l6,*l=new List<FeatureTerm>;
	FeatureTerm *ft=0;
	List<FeatureTerm> *nodes=0;
	List< List<Path> > *paths=0;
	List< List<Sort> > *mg_sorts=0;
	List< List<FeatureTerm> > *parents=0;
	List<Path> *pathl=0;
	List<Sort> *mg_sortl=0;
	List<FeatureTerm> *parentsl=0;
	Path *path=0;
	FeatureTerm *node=0;
	FeatureTerm *modified_node;
	List<FeatureTerm> nodes_already_considered_for_path_equalties;
	bool is_domain_object;

	base->nodesAllPathsSortsParents(&nodes,&paths,&mg_sorts,&parents,ontology);
	
	nodes->Rewind();
	paths->Rewind();
	mg_sorts->Rewind();
	parents->Rewind();
	while(nodes->Iterate(node) &&
		  paths->Iterate(pathl) &&
		  mg_sorts->Iterate(mg_sortl) &&
		  parents->Iterate(parentsl)) {

		path=pathl->operator [](0);

		is_domain_object=false;
		if (domain_model!=0 && domain_model->MemberP(node)) is_domain_object=true;

		if (!is_domain_object) {

			/* Domain Specializations: */ 

			if (generate_costants_refinements && objects!=0) {
				List<FeatureTerm> lobjects,*lvalues,lvalues2,*ltmp;
				FeatureTerm *object;
				FeatureTerm *value;

				lobjects.Instance(*objects);
				lobjects.Rewind();
				while(lobjects.Iterate(object)) {
					lvalues=object->readPath(path);

					while(!lvalues->EmptyP()) {
						value=lvalues->ExtractIni();
						if (value->isSet()) {
							ltmp=value->set_values();
							while(!ltmp->EmptyP()) lvalues->Add(ltmp->ExtractIni());
							delete ltmp;
							delete value;
						} else {
							if (!lvalues2.MemberP(value)) lvalues2.Add(value);
													 else delete value;
						} /* if */ 
					} /* while */ 
					delete lvalues;
					lvalues=0;
				} /* while */ 

				while(!lvalues2.EmptyP()) {
					value=lvalues2.ExtractIni();
					if (!value->isSet() && 
						node->subsumes(value) && !value->subsumes(node)) {

						if (domain_model==0 || 
							domain_model->MemberP(value) ||
							value->isInt() ||
							value->isFloat() ||
							value->isSymbol()) {
							ft=base->clone_substituting(node,value,domain_model);
							l1.Add(ft);
						} /* if */ 
					} // if  		
					delete value;
				} // while  
			} // if  




			/* Use "cloneInternal" to create clones of the base FT with the proper modifications: */ 
			if (generate_equality_refinements &&
			    (node->isAbstract() || node->isFeatureTerm() || 
				 ((node->isInt() || node->isFloat() || node->isSymbol()) && !node->hasValue()))) {

				/* Path equalties: */ 
				{
					bool test,found;
					List<FeatureTerm> *lo1,*lo2,*lo3;
					FeatureTerm *o,*o2,*o3,*o4;

					nodes_already_considered_for_path_equalties.Add(node);

					/* Add all the possible path equalties: */ 
					List<FeatureTerm> nodes2;
					List< List<Path> > paths2;
					List< List<FeatureTerm> > parents2;
					List<Path> *pathl2;
					List<FeatureTerm> *parentsl2=0;
					FeatureTerm *node2;
					Path *path2;
					bool inside_same_set=false;

					nodes2.Instance(*nodes);
					paths2.Instance(*paths);
					parents2.Instance(*parents);
					nodes2.Rewind();
					paths2.Rewind();
					parents2.Rewind();
					while(nodes2.Iterate(node2) &&
						  paths2.Iterate(pathl2) &&
						  parents2.Iterate(parentsl2)) {
						path2=pathl2->operator [](0);

						if (parentsl->Length()==1 &&
						    parentsl2->Length()==1 &&
							parentsl->operator[](0)!=0 && 
							parentsl->operator[](0)->same(parentsl2->operator[](0)) &&
							parentsl->operator[](0)->isSet()) inside_same_set=true;
														 else inside_same_set=false;

						if (!inside_same_set &&
							node!=node2 && 
							!node->same(node2) &&
							!nodes_already_considered_for_path_equalties.MemberRefP(node2) &&
							(node2->isAbstract() || node2->isFeatureTerm() || 
							 ((node2->isInt() || node2->isFloat() || node2->isSymbol()) && !node2->hasValue())) &&							
							!domain_model->MemberP(node) && !domain_model->MemberP(node2)) {
							/* Test if the path equalty appears in the base objects: */ 
							test=false;

							/* Test is the objects are unificable: */ 
							{
								Sort *s1=node->getSort(),*s2=node2->getSort();
								if (!s1->isSubsort(s2) && !s2->isSubsort(s1)) test=false;
							}

							if (objects!=0) {
								objects->Rewind();
								while(!test && objects->Iterate(o)) {
									lo1=o->readPath(path);
									lo2=o->readPath(path2);
									lo1->Rewind();
									found=false;
									while(!found && lo1->Iterate(o2)) {
										lo2->Rewind();
										while(!found && lo2->Iterate(o3)) {
											if (o2->isSet()) {
												if (o3->isSet()) {
													lo3=o2->set_values_quick();
													lo3->Rewind();
													while(lo3->Iterate(o4)) {
														if (o3->set_values_quick()->MemberP(o4)) found=true;
													} /* while */ 
													lo3=0;
												} else {
													if (o2->set_values_quick()->MemberP(o3)) found=true;
												} /* if */ 
											} else {
												if (o3->isSet()) {
													if (o3->set_values_quick()->MemberP(o2)) found=true;
												} else {
													if ((*o2)==(*o3)) found=true;
												} /* if */ 
											} /* if */ 
										} /* while */ 
									} /* while */ 
									if (found) test=true;

									delete lo1;
									delete lo2;
								} /* while */ 
							} /* if */ 

							// add path equalty: node = node2  
							if (test && !node->isSet() && !node2->isSet()) {

								if (node->subsumes(node2)) {
									ft=base->clone_linking(node,node2,domain_model);
								} else if (node2->subsumes(node)) {
									ft=base->clone_linking(node2,node,domain_model);
								} else {
									ft=0;
								} // if 
								if (ft!=0) {
									l2.Add(ft);
								} /* if */ 
							} /* if */ 
						} /* if */ 

					} /* while */ 
				}  	
			} /* if */ 
		} /* if */ 


		/* Add sets: */ 
		if (generate_sets_refinements) {
			Path *path_of_set;
			FeatureTerm *parent_of_node,*new_parent_of_node;
			Sort *mg_sort;

			pathl->Rewind();
			parentsl->Rewind();
			mg_sortl->Rewind();
			while(pathl->Iterate(path_of_set) &&
				  parentsl->Iterate(parent_of_node) &&
				  mg_sortl->Iterate(mg_sort)) {

				if (mg_sort!=0) {

					bool add_set=false;
					if (node->isAbstract() || node->isFeatureTerm() || node->isSet()) {
						if (set_paths==0) {
							add_set=true;
						} else {
							if (set_paths->MemberP(path_of_set)) add_set=true;
						} /* if */ 
					} /* if */ 

					if (add_set) {
						ft=base->clone_keepingTrackOf2Nodes(node,&modified_node,parent_of_node,&new_parent_of_node,domain_model);
						if (modified_node!=0 && (new_parent_of_node!=0 || parent_of_node==0)) {
							if (modified_node->isSet()) {
								modified_node->addSetValue(new FeatureTerm((Symbol *)0,mg_sort,0));
								l3.Add(ft);
								ft=0;
							} /* if */ 

							if (modified_node->isAbstract() || node->isFeatureTerm()) {
								if (parent_of_node==0) {
									// In the formulation presented in the report, the root node cannot be converted into a set
									// thus, for keeping both versions identical, I'commented out this part of code:
									/*
									FeatureTerm *tmp_ft=FeatureTerm::SetFeatureTerm();

									tmp_ft->addSetValue(ft);
									tmp_ft->addSetValue(new FeatureTerm((Symbol *)0,mg_sort,0));

									l3.Add(tmp_ft);
									ft=0;
									*/
								} else {
									if (new_parent_of_node->isFeatureTerm()) {									
										Symbol *feature_of_set=0;
										FeatureTerm *tmp_ft,*tmp_ft2;
										
										path_of_set->Forward();
										feature_of_set=path_of_set->GetObj();
										tmp_ft=new_parent_of_node->featureValue(feature_of_set);
										new_parent_of_node->removeFeatureValue(feature_of_set);

										tmp_ft2=FeatureTerm::SetFeatureTerm();

										tmp_ft2->addSetValue(tmp_ft);
										tmp_ft2->addSetValue(new FeatureTerm((Symbol *)0,mg_sort,0));
										new_parent_of_node->defineFeatureValue(feature_of_set,tmp_ft2);

										l3.Add(ft);
										ft=0;									
									} /* if */ 
									
									// If the parent is a set, then it is the parent itself who has to be expanded, not this node
									// For that reason, this code is comented out:
									/*
									if (new_parent_of_node->isSet()) {			
										FeatureTerm *tmp_ft,*tmp_ft2;

										tmp_ft=new FeatureTerm(modified_node);

										new_parent_of_node->removeSetValue(modified_node);

										tmp_ft2=FeatureTerm::SetFeatureTerm();

										tmp_ft2->addSetValue(tmp_ft);
										tmp_ft2->addSetValue(new FeatureTerm((Symbol *)0,mg_sort,0));
										new_parent_of_node->addSetValue(tmp_ft2);

										l3.Add(ft);
										ft=0;									
									} // if
									*/ 
								} /* if */ 
							} /* if */ 

							if (ft!=0) delete ft;
						} else {
							delete ft;
						} /* if */ 				
					} /* if */ 
				} /* if */ 

			} /* while */ 
		}


		if (!is_domain_object) {
			if (node->isAbstract() || node->isFeatureTerm() || !node->hasValue()) {

				/* Sort Specializations: */ 
				if (generate_sort_refinements) {
					List<Sort> *sl;
					Sort *sort=node->getSort();
					Sort *subsort;
					sl=sort->getSubSorts();

					sl->Rewind();
					while(sl->Iterate(subsort)) {
						ft=new FeatureTerm(node);
						modified_node=ft->clone(domain_model);
						delete ft;
						modified_node->setSort(subsort);
						ft=base->clone_substituting(node,modified_node,domain_model);
						delete modified_node;
						l4.Add(ft);
					} // while 

					delete sl;		
				} // if

				/* Add Features: */ 
				if (generate_feature_refinements) {
					FeatureTerm *o,*ft_tmp;
					List<FeatureTerm> *lo;
					Sort *sort=node->getSort();
					Sort *fsort;
					List<Symbol> *fnames_l;
					Symbol *fname,*fname2;
					Path *path2;
					bool test;
		
					path2=new Path(*path);
					
					fnames_l=sort->getFeatures();
					fnames_l->Rewind();
					while(fnames_l->Iterate(fname)) {
						ft_tmp=node->featureValue(fname);
						if (ft_tmp==0) {
							/* test if the base objects have this feature: */ 
							test=false;

							if (objects!=0) {
								path2->Add(new Symbol(fname));
								objects->Rewind();
								while(!test && objects->Iterate(o)) {
									lo=o->readPath(path2);
									if (!lo->EmptyP()) test=true;
									delete lo;
								} /* while */ 
								fname2=path2->Extract();
								delete fname2;
							} /* if */ 

							if (test) {
								ft=base->clone_keepingTrackOfNode(node,&modified_node,domain_model);
								if (modified_node!=0) {
									fsort=sort->featureSort(fname);
									modified_node->defineFeatureValue(fname,new FeatureTerm((Symbol *)0,fsort,0));
									l5.Add(ft);
								} else {
									delete ft;
								} /* if */ 
							} /* if */ 
						} else {
							delete ft_tmp;
						} // if  
					} // while  

					delete path2;
					delete fnames_l;
				} // if

			} /* if */ 

		} // if


		// Special refinements:
/*		if (!node->hasValue()) {
			// Type 1, create a new special term:
			Sort *s=node->getSort();
			List<Sort> *l=s->getSubSorts();
			FeatureTerm *f;

			l->Rewind();
			while(l->Iterate(s)) {
				if (s->get_data_type()==DATATYPE_SPECIAL) {
					f=new FeatureTerm((Symbol *)0,s->createSpecial(0,0),s);
					ft=base->clone_substituting(node,f,domain_model);
					l6.Add(ft);
				} // if 
			} // while 
			delete l;
		} // if 
*/		if (node->isSpecial()) {
			// Type 2, refine an existing special term:

			List<FeatureTerm> lobjects,*lvalues,lvalues2,*ltmp;
			FeatureTerm *object;
			FeatureTerm *value;

			lobjects.Instance(*objects);
			lobjects.Rewind();
			while(lobjects.Iterate(object)) {
				lvalues=object->readPath(path);

				while(!lvalues->EmptyP()) {
					value=lvalues->ExtractIni();
					if (value->isSet()) {
						ltmp=value->set_values();
						while(!ltmp->EmptyP()) lvalues->Add(ltmp->ExtractIni());
						delete ltmp;
						delete value;
					} else {
						if (!lvalues2.MemberP(value)) lvalues2.Add(value);
												 else delete value;
					} /* if */ 
				} /* while */ 
				delete lvalues;
				lvalues=0;
			} /* while */ 

			ltmp=node->getSpecialTerm()->DSpecialization_b(&lvalues2,ontology,domain_model);

			if (ltmp!=0) {
				while(!ltmp->EmptyP()) {
					value=ltmp->ExtractIni();
					ft=base->clone_substituting(node,value,domain_model);
					l6.Add(ft);
					delete value;
				} // while 
				delete ltmp;
			} // if 
		} // if 

	} /* while */ 

	while(!nodes_already_considered_for_path_equalties.EmptyP()) nodes_already_considered_for_path_equalties.ExtractIni();								

	while(!nodes->EmptyP()) nodes->ExtractIni();
	delete nodes;

	delete paths;
	while(!mg_sorts->EmptyP()) {
		mg_sortl=mg_sorts->ExtractIni();
		while(!mg_sortl->EmptyP()) mg_sortl->ExtractIni();
		delete mg_sortl;
	} /* while */ 
	delete mg_sorts;
	delete parents;

	/* This is done to keep ordered the specializations: DOMAIN -> PATH -> SET -> SORT -> FEATURES -> SPECIAL*/ 
//	printf("%i - %i - %i - %i - %i - %i\n",l1.Length(),l2.Length(),l3.Length(),l4.Length(),l5.Length(),l6.Length());
//	fflush(0);	
	while(!l1.EmptyP()) l->Add(l1.ExtractIni());
	while(!l2.EmptyP()) l->Add(l2.ExtractIni());
	while(!l3.EmptyP()) l->Add(l3.ExtractIni());
	while(!l4.EmptyP()) l->Add(l4.ExtractIni());
	while(!l5.EmptyP()) l->Add(l5.ExtractIni());
	while(!l6.EmptyP()) l->Add(l6.ExtractIni());

	return l;

} /* DSpecialization_b */






